/*
 * File:				vfs.c
 * Created:				August 2008
 * Created by:			Axel von Bertoldi
 * Last Modified:		March 2008
 * Last Modified by:	Axel von Bertoldi
 * (C) 2005-2008		Axel von Bertoldi
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to:
 * The Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor
 * Boston, MA 02110-1301, USA.
 */

#include <gio/gdesktopappinfo.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <gio/gio.h>

#include "vfs.h"
#include "utils.h"
#include "config.h"

/******************************************************************************/
/* sort the structures based on the file's display_name */
static inline gint
vfs_sort_array (const VfsFileInfo **i1, const VfsFileInfo **i2) {
	return g_utf8_collate ((gchar *)(*i1)->display_name, (gchar *)(*i2)->display_name);
}
/******************************************************************************/
gboolean
vfs_file_exists (const gchar *file_name) {
	return g_file_test (file_name, G_FILE_TEST_EXISTS);
}
/******************************************************************************/
gboolean
vfs_file_is_executable (const gchar *file_name) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	GFile*	   file = g_file_new_for_path (file_name);
	GFileInfo* file_info =  g_file_query_info (file,
											   /*G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE ,*/
											   "standard::content-type,"
											   "access::can-execute",
											   0,
											   NULL,
											   NULL);

	gboolean ret = g_file_info_get_attribute_boolean (file_info,
													  G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE) &&
													  g_content_type_can_be_executable (g_file_info_get_content_type (file_info));

	g_object_unref (file_info);
	g_object_unref (file);
	return ret;	
}
/******************************************************************************/
gboolean
vfs_file_is_directory (const gchar *file_name) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	GFile*	   file = g_file_new_for_path (file_name);
	GFileInfo* file_info =  g_file_query_info (file,
											   G_FILE_ATTRIBUTE_STANDARD_TYPE,
											   0,
											   NULL,
											   NULL);

	gboolean ret = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY);
	g_object_unref (file_info);
	g_object_unref (file);
	return ret;
}
/******************************************************************************/
gboolean
vfs_file_is_desktop (const gchar *file_name) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	GDesktopAppInfo *app_info = g_desktop_app_info_new_from_filename (file_name);
	gboolean ret = !(app_info == NULL);
	app_info ? g_object_unref (app_info) : NULL;
	return ret;
}
/******************************************************************************/
/* Gets the executable file name of the application associated  with the passed
 * file. The caller must free the returned value. */
gchar*
vfs_get_default_mime_application (const gchar *file_name) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	GFile*	   file = g_file_new_for_path (file_name);
	GFileInfo* file_info =  g_file_query_info (file,
											   G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
											   0,
											   NULL,											   
											   NULL);

	const gchar* content_type = g_file_info_get_content_type (file_info);
	GAppInfo *app_info = g_app_info_get_default_for_type (content_type, FALSE);
	gchar* exec = g_strdup (g_app_info_get_executable (app_info));

	g_object_unref (file_info);
	g_object_unref (file);
	g_object_unref (app_info);

	return exec;
}
/******************************************************************************/
 /* The caller  must free the return value. */
static gchar *
vfs_get_desktop_app_name (const gchar *file_name) {
	GDesktopAppInfo *info = g_desktop_app_info_new_from_filename (file_name);
	gchar* ret = g_strdup (g_app_info_get_name (G_APP_INFO (info)));
	g_object_unref (info);
	return ret;
}
/******************************************************************************/
void
vfs_file_trash (gchar *file_name) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	GError *error = NULL;
	GFile *file = g_file_new_for_path (file_name);

	/* Try moving it to the trash */
	g_file_trash (file, NULL, &error);

	/* Let the user know if we failed. */
	utils_gerror_ok (&error, TRUE);
	g_free (file_name);
}
/******************************************************************************/
GtkWidget *
vfs_get_icon_for_file (const gchar *file_name) {

	GtkWidget *icon_widget = NULL;
	GDesktopAppInfo *app_info = g_desktop_app_info_new_from_filename (file_name);

	if (app_info != NULL) {
		GIcon *icon = g_app_info_get_icon (G_APP_INFO (app_info));
		icon_widget = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
		g_object_unref (app_info);
	}
	else {
		GFile *file = g_file_new_for_path (file_name);
		GFileInfo *file_info = g_file_query_info (file,
											  G_FILE_ATTRIBUTE_STANDARD_ICON,
											  0,
											  NULL,
											  NULL);	

		GIcon *icon = g_file_info_get_icon (file_info);
		icon_widget = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
		g_object_unref (file);
		g_object_unref (file_info);
	}
	return icon_widget;
}
/******************************************************************************/
GList*
vfs_get_all_mime_applications (const gchar *file_name) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	GFile*	   file = g_file_new_for_path (file_name);
	GFileInfo* file_info =  g_file_query_info (file,
											   G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
											   0,
											   NULL,
											   NULL);

	const gchar* content_type = g_file_info_get_content_type (file_info);
	GList *app_infos = g_app_info_get_all_for_type (content_type);
	
	g_object_unref (file_info);
	g_object_unref (file);

	return app_infos;
}
/******************************************************************************/
/* Takes a null-terminated array of strings at least 2 items long. The first
 * item is always the application to execute, including if it is a script or
 * .desktop file. The second is the files to open. Either ARG_FILE or ARG_APP
 * can be NULL, but not both. If ARG_FILE is NULL, just launch ARG_APP. If
 * ARG_APP is NULL, open ARG_FILE with the default mime handler. */
gboolean
vfs_launch_application (const gchar *const *args) {

	/* Can't have both arg[ARG_APP] and args[ARG_FILE} NULL */
	g_return_val_if_fail ((args[ARG_APP] != NULL || args[ARG_FILE] != NULL), FALSE);

	guint	 i;
	gboolean ret	   = FALSE;
	GError	 *error    = NULL;
	GList	 *files	   = NULL;
	gchar	 *command_line = NULL;

	/* Put the file(s) in a list (only of there is a file) for g_app_info_launch() */
	for (i = ARG_FILE; args[i]; i++) {
		files = g_list_append (files, (gpointer)g_file_new_for_path (args[i]));
	}

    gchar *uri = args[ARG_FILE] ? g_filename_to_uri(args[ARG_FILE], NULL, NULL) : g_filename_to_uri(args[ARG_APP], NULL, NULL);
    GtkRecentManager *recent = gtk_recent_manager_new ();
    gtk_recent_manager_add_item (recent, uri);
    g_object_unref (G_OBJECT(recent));
    g_free (uri);

	/* Set the current working dir. Do we use ARG_APP or ARG_FILE to set
	 * cwd?  If ARG_FILE == NULL, use ARG_APP, otherwise use ARG_FILE */
	const gchar *cwd_root = args[ARG_FILE] == NULL ? args[ARG_APP] : args[ARG_FILE];
	gchar *working_dir = vfs_file_is_directory (cwd_root) ? g_strdup (cwd_root) : g_path_get_dirname (cwd_root);
	g_chdir (working_dir);
	g_free (working_dir);

	/* No agrs[ARG_APP] was NULL, open file with default mime handler */
	if (args[ARG_APP] == NULL) {
		GFile *file = g_file_new_for_path (args[ARG_FILE]);
		gchar *uri = g_file_get_uri (file);
		ret = g_app_info_launch_default_for_uri (uri, NULL, &error);
		utils_gerror_ok (&error, TRUE);
		g_free (uri);
		g_object_unref (file);
	}
	else {
		GAppInfo *app_info = NULL;
		if (vfs_file_is_desktop (args[ARG_APP])) {
			app_info = G_APP_INFO (g_desktop_app_info_new_from_filename (args[ARG_APP]));
		}
		else {
			command_line = vfs_file_is_executable (args[ARG_APP]) ?
				utils_escape_str (args[ARG_APP], " ", "\\ ") : g_strdup (args[ARG_APP]);
			app_info = g_app_info_create_from_commandline (command_line, NULL, 0, &error);
			utils_gerror_ok (&error, TRUE);
		}

		if (app_info) {
			ret = g_app_info_launch (app_info, files, NULL, &error);
			utils_gerror_ok (&error, TRUE);
			g_object_unref (app_info);
		}
		else {
			gchar *msg = g_strdup_printf (_("Could not open \"%s\".\n"), args[ARG_FILE]);
			utils_show_dialog (_("Error"), msg, GTK_MESSAGE_ERROR);
			g_free (msg);
		}
	}
	g_list_foreach (files, (GFunc)g_object_unref, NULL);
	g_list_free (files);
	g_free (command_line);

	g_chdir (g_get_home_dir());

	return ret;
}
/******************************************************************************/
gboolean
vfs_file_do_default_action (const gchar *file_name) {
	gchar **args = g_strv_new (ARGS_SIZE);
	gchar *_file_name = g_strdup (file_name);

	if (vfs_file_is_desktop (_file_name) ||
		vfs_file_is_executable (_file_name)) {
		args[ARG_APP] = _file_name;
	}
	/* open it with the default mime app */
	else {
		args[ARG_FILE] = _file_name;
	}
	gboolean ret = vfs_launch_application ((const gchar*const*)args);
	g_strfreev (args);
	return ret;
}
/******************************************************************************/
/* Gets the contents of a directory. Puts the files and directories in separate
 * arrays, and sorts them both. The caller must free the return value. */
gchar*
vfs_get_dir_listings (GPtrArray *files,
					  GPtrArray *dirs,
					  gboolean show_hidden,
					  const gchar *path) {
	if (DEBUG) g_printf ("In %s\n", __FUNCTION__);

	GError *error = NULL;

	GFile *file = g_file_new_for_path (path);
	/* get ALL the info about the files */
	GFileEnumerator *enumerator = g_file_enumerate_children (file,
															 /*"*",*/
															 "standard::type,"
															 "standard::is-hidden,"
															 "standard::name,"
															 "standard::display-name,"
															 "access::can-execute",
															 0,
															 NULL,
															 &error);
	/* did we read the dir correctly? */
	if (error) {
		gchar *error_msg = g_strdup (error->message);
		utils_gerror_ok (&error, FALSE);
		return error_msg;
	}

	GFileInfo *file_info = NULL;
	while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL) {
		/* skip the file if it's hidden and we aren't showing hidden files */
		if (g_file_info_get_is_hidden (file_info) && !show_hidden) {
			g_object_unref (file_info);
			continue;
		}
		VfsFileInfo *vfs_file_info = g_new0 (VfsFileInfo ,1);

		vfs_file_info->file_name = g_strdup_printf ("%s/%s", path, g_file_info_get_name (file_info));

		/* get the file's human readable name, including if it's a desktop file */

		
		vfs_file_info->display_name = (vfs_file_is_desktop (vfs_file_info->file_name)) ?
									   vfs_get_desktop_app_name (vfs_file_info->file_name) :
									   g_strdup (g_file_info_get_display_name (file_info));

		vfs_file_info->is_executable = g_file_info_get_attribute_boolean (file_info,
																		  G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE) &&
											(g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY);
		/* get the icon */
		vfs_file_info->icon = vfs_get_icon_for_file (vfs_file_info->file_name); 

		/* add it to the array */
		if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) {
			g_ptr_array_add (dirs, (gpointer)vfs_file_info);
		}
		else {
			g_ptr_array_add (files, (gpointer)vfs_file_info);
		}
		g_object_unref (file_info);
	}
	g_object_unref (enumerator);
	g_object_unref (file);

	/* always check for errors */
	if (error) {
		gchar *error_msg = g_strdup (error->message);
		utils_gerror_ok (&error, FALSE);
		return error_msg;
	}

	g_ptr_array_sort (dirs, (GCompareFunc)&vfs_sort_array);
	g_ptr_array_sort (files, (GCompareFunc)&vfs_sort_array);

	return NULL;
}
/******************************************************************************/
